// File: jmcomic-core/src/main/java/dev/jukomu/common/util/system/ConfigTemplate.java
package dev.jukomu.common.util.system;

import dev.jukomu.common.base.packer.PackerUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 对应Python的ConfigTemplate，用于管理配置文件。
 * 支持从文件加载、保存，以及默认值提供。
 */
public class ConfigTemplate {

    // 默认配置提供者，可以是Map或另一个ConfigTemplate实例
    // Python中默认是dict，这里也用Map
    private Map<String, Object> defaultProvider;

    private Map<String, Object> metadata; // 实际配置数据
    private String filepath; // 配置文件路径

    /**
     * 构造函数。
     *
     * @param metadata 配置数据Map
     * @param filepath 配置文件路径（可选）
     */
    public ConfigTemplate(Map<String, Object> metadata, String filepath) {
        Objects.requireNonNull(metadata, "Metadata cannot be null.");
        this.metadata = metadata;
        this.filepath = filepath;
        this.defaultProvider = new HashMap<>(); // 默认为空Map，可以后续设置
    }

    /**
     * 构造函数，不指定文件路径。
     *
     * @param metadata 配置数据Map
     */
    public ConfigTemplate(Map<String, Object> metadata) {
        this(metadata, null);
    }

    /**
     * 获取文件路径。
     *
     * @return 文件路径
     */
    public String getFilepath() {
        return filepath;
    }

    /**
     * 获取配置值。
     * 对应Python的__getattr__和__getitem__。
     *
     * @param key 键
     * @return 配置值
     * @throws java.util.NoSuchElementException 如果键不存在且没有默认提供者
     */
    public Object get(String key) {
        if (metadata.containsKey(key)) {
            return metadata.get(key);
        }

        // 如果没有默认提供者，或者默认提供者是自身（即没有其他默认值来源），则抛出异常
        if (defaultProvider == null || defaultProvider.isEmpty()) {
            throw new java.util.NoSuchElementException("Key not found: " + key);
        }

        return fromDefault(key);
    }

    /**
     * 获取配置值，如果不存在则返回默认值。
     *
     * @param key          键
     * @param defaultValue 默认值
     * @param <T>          返回类型
     * @return 配置值或默认值
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, T defaultValue) {
        if (metadata.containsKey(key)) {
            return (T) metadata.get(key);
        }

        if (defaultProvider == null || defaultProvider.isEmpty()) {
            return defaultValue;
        }

        try {
            return (T) fromDefault(key);
        } catch (java.util.NoSuchElementException e) {
            return defaultValue; // Default provider didn't have it either
        }
    }


    /**
     * 设置配置值。
     * 对应Python的__setattr__和__setitem__。
     *
     * @param key   键
     * @param value 值
     */
    public void set(String key, Object value) {
        metadata.put(key, value);
    }

    /**
     * 获取原始的metadata Map。
     *
     * @return metadata Map
     */
    public Map<String, Object> getMetadata() {
        return metadata;
    }

    /**
     * 设置默认配置提供者。
     *
     * @param defaultProvider 默认配置Map
     */
    public void setDefaultProvider(Map<String, Object> defaultProvider) {
        this.defaultProvider = defaultProvider;
    }

    /**
     * 获取包含所有默认值和自定义值的完整metadata Map。
     *
     * @return 完整的metadata Map
     */
    public Map<String, Object> toFullMetadata() {
        Map<String, Object> full = new HashMap<>(defaultProvider);
        full.putAll(metadata); // 自定义值覆盖默认值
        return full;
    }

    /**
     * 移除与默认值相同的自定义配置。
     */
    public void removeSelfDefault() {
        for (String key : new HashMap<>(metadata).keySet()) { // Iterate over a copy to allow modification
            try {
                Object defaultValue = fromDefault(key);
                if (Objects.equals(metadata.get(key), defaultValue)) {
                    metadata.remove(key);
                }
            } catch (java.util.NoSuchElementException | ClassCastException e) {
                // Ignore keys not found in default provider or type mismatch
                continue;
            }
        }
    }

    /**
     * 从默认提供者中获取值。
     *
     * @param key 键
     * @return 默认值
     * @throws java.util.NoSuchElementException 如果默认提供者中不存在该键
     */
    protected Object fromDefault(String key) {
        if (defaultProvider.containsKey(key)) {
            return defaultProvider.get(key);
        }
        throw new java.util.NoSuchElementException("Key not found in default provider: " + key);
    }

    /**
     * 保存配置到文件。
     *
     * @param filepath 文件路径（可选），如果为null则使用ConfigTemplate内部路径
     * @throws IOException              如果保存失败
     * @throws IllegalArgumentException 如果未指定文件路径
     */
    public void toFile(String filepath) throws IOException {
        String finalFilepath = (filepath != null) ? filepath : this.filepath;
        if (finalFilepath == null) {
            throw new IllegalArgumentException("Filepath is null, cannot save config.");
        }
        PackerUtils.pack(metadata, finalFilepath);
        this.filepath = finalFilepath; // 更新内部文件路径
    }

    /**
     * 保存配置到ConfigTemplate内部记录的文件路径。
     *
     * @throws IOException              如果保存失败
     * @throws IllegalArgumentException 如果未指定文件路径
     */
    public void toFile() throws IOException {
        toFile(null);
    }

    /**
     * 从文件加载配置。
     *
     * @param filepath 文件路径
     * @param <T>      返回类型 (通常是ConfigTemplate本身)
     * @return ConfigTemplate实例
     * @throws IOException            如果加载失败
     * @throws ClassNotFoundException 如果反序列化过程中找不到类
     */
    @SuppressWarnings("unchecked")
    public static <T extends ConfigTemplate> T fromFile(String filepath) throws IOException, ClassNotFoundException {
        // PackerUtils.unpack returns Object, which could be Map or other types.
        // We expect it to be a Map<String, Object> for ConfigTemplate.
        Object unpacked = PackerUtils.unpack(filepath);
        if (!(unpacked instanceof Map)) {
            throw new IOException("Loaded data from " + filepath + " is not a Map.");
        }
        return (T) new ConfigTemplate((Map<String, Object>) unpacked, filepath);
    }

    @Override
    public String toString() {
        return "ConfigTemplate{" +
                "metadata=" + metadata +
                ", filepath='" + filepath + '\'' +
                '}';
    }
}